package com.zhiyou100.test.test02.homework;

import cn.hutool.core.convert.Convert;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author yang
 * @className XmlAll
 * @packageName com.zhiyou100.test.test02.homework
 * @Description TODO xml 解析 反射通用
 * @date 2020-08-10 09:06
 */
public class XmlAll {
    private String path;

    public XmlAll() {
    }

    public XmlAll(String path) {
        this.path = path;
    }

    /**
     * @return T
     * @Description TODO 查询一个
     * @name queryOne
     * @param: [t, from]
     * @date 8/10/20 15:57
     * @author yang
     */
    public <T> T queryOne(Class<T> t, int from) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        if (from == 0) {
            return null;
        }

        // 获取根节点
        final Element root = getDocument().getRootElement();

        // 获取所有的 student 节点
        List elements = root.elements(t.getSimpleName());

        // 找到
        for (Object element : elements) {

            // 创建一个对象
            final T object = t.newInstance();

            // 转型
            Element o = (Element) (element);


            // 获取 element 的 sid 的 值
            int elementSid = Integer.parseInt(o.attribute(0).getValue());

            // sid 设置给 object
            final Field sid = t.getDeclaredField(o.attribute(0).getName());

            // 暴力访问
            sid.setAccessible(true);

            // 注入
            sid.set(object, elementSid);

            // 找到一致的 sid
            if (from == elementSid) {
                // 遍历赋值

                myForEach(t, object, o);

                // 跳出
                return object;
            }
        }
        return null;
    }

    /**
     * @return void
     * @Description TODO 根据根据 id 获取一个
     * @name delOne
     * @param: [t, from]
     * @date 8/10/20 19:25
     * @author yang
     */
    public <T> void delOne(Class<T> t, int from) throws IllegalAccessException, NoSuchFieldException, InstantiationException {
        if (queryOne(t, from) == null) {
            // 不存在，跳出
            return;
        }
        //  存在，删除

        // 获取根节点
        final Document document = getDocument();
        final Element root = document.getRootElement();

        // 获取所有的 student 节点
        List elements = root.elements(t.getSimpleName());

        // 找到
        for (Object element : elements) {


            // 转型
            Element o = (Element) (element);


            // 获取 element 的 sid 的 值
            int elementSid = Integer.parseInt(o.attribute(0).getValue());

            if (from == elementSid) {
                // sid 一致，说明是同一个,删除，写回去，退出
                elements.remove(o);
                setDocument(document);
                return;
            }
        }


    }

    /**
     * @return java.util.ArrayList<T>
     * @Description TODO 获取所有
     * @name getAll
     * @param: [t]
     * @date 8/10/20 19:25
     * @author yang
     */
    public <T> ArrayList<T> getAll(Class<T> t) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        ArrayList<T> ts = new ArrayList<>();

        // 获取根节点
        final Element root = getDocument().getRootElement();

        // 获取所有的 student 节点
        List elements = root.elements(t.getSimpleName());

        // 找到
        for (Object element : elements) {

            // 创建一个对象
            T object = t.newInstance();

            // 转型
            Element o = (Element) (element);


            // 获取 element 的 sid 的 值
            int elementSid = Integer.parseInt(o.attribute(0).getValue());

            // sid 设置给 object
            final Field sid = t.getDeclaredField(o.attribute(0).getName());

            // 暴力访问
            sid.setAccessible(true);

            // 注入
            sid.set(object, elementSid);
            myForEach(t, object, o);
            ts.add(object);

        }

        return ts;
    }

    /**
     * @return int
     * @Description TODO 获取最的 id
     * @name getMaxId
     * @param: [t]
     * @date 8/10/20 19:42
     * @author yang
     */
    public <T> int getMaxId(Class<T> t) throws Exception {
        // 记录最大的 sid
        int maxSid = 0;

        // 获取根节点
        Element rootElement = getDocument().getRootElement();

        // 获取所有的 student 节点
        List student = rootElement.elements(t.getSimpleName());

        // 找到到最大的
        for (Object o : student) {
            if (o instanceof Element) {
                // 转型
                Element element = (Element) (o);
                // 获取 属性 sid 的值
                int sid = Integer.parseInt(element.attribute(0).getValue());
                maxSid = Math.max(sid, maxSid);
            }
        }

        return maxSid;
    }

    /**
     * @return void
     * @Description TODO 添加一个
     * @name addOne
     * @param: [t, o]
     * @date 8/10/20 20:13
     * @author yang
     */
    public <T> void addOne(Class<T> t, T o) throws Exception {

        // 获取 document
        Document document = getDocument();
        //  获取根标签
        final Element root = document.getRootElement();

        // 添加一个标签
        final Element student = root.addElement(t.getSimpleName());

        // 获取实体类的第一个字段
        final Field field = t.getDeclaredFields()[0];
        field.setAccessible(true);
        // 获取 id 属性的 名字
        final String name = field.getName();

        // 设置属性
        student.addAttribute(name, (getMaxId(t) + 1) + "");


        // 添加所有的子标签，设置文本内容
        for (Field declaredField : t.getDeclaredFields()) {
            //     遍历所有字段
            if (!declaredField.getName().trim().equals(name)) {
                // 不添加 id 标签
                final Field declaredField1 = o.getClass().getDeclaredField(declaredField.getName());
                declaredField1.setAccessible(true);
                final Object o1 = declaredField1.get(o);
                student.addElement(declaredField.getName()).setText(o1 + "");
            } else {
                // 如果 id 不为空，设置
                final Field declaredField1 = o.getClass().getDeclaredField(declaredField.getName());
                declaredField1.setAccessible(true);
                final Object o1 = declaredField1.get(o);
                if (o1 != null) {
                    student.attribute(declaredField.getName()).setText(o1 + "");
                }
            }
        }
        // 设置
        setDocument(document);

    }

    /**
     * @return void
     * @Description TODO 改变一个
     * @name changeOne
     * @param: [t, from]
     * @date 8/10/20 19:34
     * @author yang
     */
    public <T> void changeOne(Class<T> t, T from) throws Exception {
        final Document document = getDocument();
        // 获取 id 属性的 名字
        final String name = ((Element) (document.getRootElement().elements(t.getSimpleName()).get(0))).attribute(0).getName();
        final Field field = from.getClass().getDeclaredField(name);
        field.setAccessible(true);
        if (field.get(from) == null) {
            // 没有传入 sid ，终止
            return;
        }
        int sid = Convert.toInt(field.get(from));
        // 删除
        delOne(t, sid);
        // 添加
        addOne(t, from);
    }


    /**
     * @return void
     * @Description TODO 遍历赋值
     * @name myForEach
     * @param: [t, object, o]
     * @date 8/10/20 12:01
     * @author yang
     */
    public <T> void myForEach(Class<T> t, T object, Element o) throws NoSuchFieldException, IllegalAccessException {
        for (Object a : o.elements()) {
            // 每一个 Element 对象
            final Element a1 = (Element) (a);

            // 根据 element 的名字获取字段
            final Field field = t.getDeclaredField(a1.getName());
            // 暴力
            field.setAccessible(true);
            // 类型转换 注入
            //field.set(object, getVal(a1.getTextTrim(), field.getType()));
            field.set(object, Convert.convert(field.getType(), a1.getTextTrim()));
        }
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    /**
     * @return org.dom4j.Document
     * @Description TODO 根据 path 获取一个Document 对象
     * @name getDocument
     * @param: []
     * @date 8/10/20 09:15
     * @author yang
     */
    public Document getDocument() {
        try {
            return new SAXReader().read(path);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @return void
     * @Description TODO 把 Document 对象设置回 xml 文件
     * @name setDocument
     * @param: [document]
     * @date 8/10/20 09:21
     * @author yang
     */
    public void setDocument(Document document) {
        // createPrettyPrint 设置不紧凑
        OutputFormat compactFormat = OutputFormat.createPrettyPrint();
        // createCompactFormat 设置紧凑
        //OutputFormat compactFormat = OutputFormat.createCompactFormat();
        compactFormat.setEncoding("utf-8");
        XMLWriter xmlWriter = null;
        try (
                // 此代码块会自动关流
                FileOutputStream fileOutputStream = new FileOutputStream(path);
                final OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream)
        ) {
            xmlWriter = new XMLWriter(outputStreamWriter, compactFormat);
            xmlWriter.write(document);
            xmlWriter.flush();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                assert xmlWriter != null;
                xmlWriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @return T
     * @Description TODO 根据 type 把 val 转为对应的类型
     * @name getVal
     * @param: [val, type]
     * @date 8/10/20 11:52
     * @author yang
     */
    public <T> T getVal(String val, Class<T> type) {
        // 把val转换成type类型返回 比如说getVal("123",Integer.class) 返回一个123
        T value = null;
        try {
            Constructor<T> constructor = type.getConstructor(String.class);
            constructor.setAccessible(true);
            value = constructor.newInstance(val);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;

    }


}
